using System;
using System.Xml.Serialization;

namespace SharpMud.MudLib.Core.Time
{
	/// <summary>
	/// Represents a duration of time in the *mud* world
	/// </summary>
	[XmlRoot("MudTimeSpan")]
	public class MudTimeSpan : System.IComparable
	{
		private long _TotalTicks;

		internal static long TimeToTicks(int hours, int ticks)
		{
			return (MudTimeSpan.TicksPerHour * (long)hours) +
				(long)ticks;
		}

		
		#region Static Comparing
		public static int Compare(MudTimeSpan ts1, MudTimeSpan ts2)
		{
			if(ts1.TotalTicks > ts2.TotalTicks)
				return 1;
			else if(ts1.TotalTicks < ts2.TotalTicks)
				return -1;
			else
				return 0;
		}

		public static bool Equals(MudTimeSpan ts1, MudTimeSpan ts2)
		{
			if(ts1.TotalTicks==ts2.TotalTicks)
				return true;
			else
				return false;
		}
		#endregion

		#region Instance Math and Comparing
		public MudTimeSpan Negate()
		{
			return new MudTimeSpan(-this.TotalTicks);
		}

		public MudTimeSpan Subtract(MudTimeSpan ts)
		{
			return new MudTimeSpan(this.TotalTicks - ts.TotalTicks);
		}

		public MudTimeSpan Add(MudTimeSpan ts)
		{
			return new MudTimeSpan(this.TotalTicks + ts.TotalTicks);
		}
		public MudTimeSpan Duration()
		{
			return new MudTimeSpan(Math.Abs(this.TotalTicks));
		}

		public new bool Equals(object obj)
		{
			if(obj.GetType()==typeof(MudTimeSpan))
			{
				return this.Equals((MudTimeSpan)obj);
			}
			else
			{	return base.Equals(obj);	}
		}
		public bool Equals(MudTimeSpan obj)
		{
			return MudTimeSpan.Equals(this,obj);
		}
		#endregion

		#region Constructors and Static Instanciation
		public MudTimeSpan(int days, int hours, int ticks)
		{
			this._TotalTicks = (days * MudTimeSpan.TicksPerDay) + (hours * MudTimeSpan.TicksPerHour) + ticks;
		}
		public MudTimeSpan(int hours, int ticks)
		{
			this._TotalTicks = (hours * MudTimeSpan.TicksPerHour) + ticks;
		}
		public MudTimeSpan(long ticks)
		{
			this._TotalTicks = (ticks);
		}

		public static MudTimeSpan FromDays(double days)
		{
			return MudTimeSpan.FromTicks((long)(days * (double)MudTimeSpan.TicksPerDay));
		}
		public static MudTimeSpan FromHours(double hours)
		{
			return MudTimeSpan.FromTicks((long)(hours * (double)MudTimeSpan.TicksPerHour));
		}
		public static MudTimeSpan FromTicks(long ticks)
		{
			return new MudTimeSpan(ticks);
		}
		
		public MudTimeSpan()
		{
		}
		#endregion

		#region Portional Properties
		[XmlElement("Days")]
		public int Days
		{
			get
			{
				return (int)(this.TotalTicks / MudTimeSpan.TicksPerDay);
			}
			set
			{
				this._TotalTicks = new MudTimeSpan(value,this.Hours,this.Ticks).TotalTicks;
			}
		}
		[XmlElement("Hours")]
		public int Hours
		{
			get
			{
				long rt = (long)((double)this.TotalTicks - ((double)this.Days * (double)MudTimeSpan.TicksPerDay));
				return (int)(rt / MudTimeSpan.TicksPerHour);
			}
			set
			{
				this._TotalTicks = new MudTimeSpan(this.Days,value,this.Ticks).TotalTicks;
			}
		}
		[XmlElement("Ticks")]
		public int Ticks
		{
			get
			{
				long rt = (long)((double)this.TotalTicks - (
					((double)this.Days * (double)MudTimeSpan.TicksPerDay)
					+ ((double)this.Hours * (double)MudTimeSpan.HoursPerDay)
					));
				return (int)(rt / MudTimeSpan.TicksPerHour);				
			}
			set
			{
				this._TotalTicks = new MudTimeSpan(this.Days,this.Hours,value).TotalTicks;
			}
		}
		#endregion

		#region Totally-Encompassing Properties
		public double TotalDays
		{
			get
			{
				return ((double)MudTimeSpan.TicksPerDay / (double)this.TotalTicks);
			}
		}

		public double TotalHours
		{
			get
			{
				return ((double)MudTimeSpan.TicksPerHour / (double)this.TotalTicks);
			}
		}
		public long TotalTicks
		{
			get
			{
				return this._TotalTicks;
			}
		}
		
		#endregion

		#region Globally Standard Instances
		public static MudTimeSpan MinValue
		{
			get
			{
				return MaxValue.Negate();
			}
		}
		public static MudTimeSpan MaxValue
		{
			get
			{
				return new MudTimeSpan(int.MaxValue,MudTimeSpan.HoursPerDay,(int)MudTimeSpan.TicksPerHour);
			}
		}
		public static MudTimeSpan Zero
		{
			get
			{
				return new MudTimeSpan(0);
			}
		}
		#endregion

		#region IComparable Members
		public int CompareTo(object obj)
		{
			if(obj.GetType()==typeof(MudTimeSpan))
				return this.CompareTo((MudTimeSpan)obj);
			else
				throw new InvalidOperationException("Attempt to compare a MudTimeSpan to an incompatible object.");
		}

		public int CompareTo(MudTimeSpan ts)
		{
			return MudTimeSpan.Compare(this,ts);
		}		
		#endregion

		#region Static "Constants"
		public static long TicksPerHour
		{
			get
			{
				return MudTimeConfiguration.Current.TicksPerHour;
			}
		}
		public static long TicksPerDay
		{
			get
			{
				return MudTimeConfiguration.Current.TicksPerDay;
			}
		}
		public static int HoursPerDay
		{
			get
			{
				return MudTimeConfiguration.Current.HoursPerDay;
			}
		}

		#endregion
		
		#region Operator Overloads
		public static MudTimeSpan operator+(MudTimeSpan ts1, MudTimeSpan ts2)
		{
			return ts1.Add(ts2);
		}

		public static MudTimeSpan operator-(MudTimeSpan ts1, MudTimeSpan ts2)
		{
			return ts1.Subtract(ts2);
		}

		#endregion

		#region Real-TimeSpan Conversions
		public static explicit operator System.TimeSpan(MudTimeSpan ts)
		{
			double tsecs = ((double)MudTimeConfiguration.Current.RealSecondsPerTick 
				* (double)ts.TotalTicks);
			return System.TimeSpan.FromSeconds(tsecs);
		}
		public static explicit operator MudTimeSpan(System.TimeSpan ts)
		{
			double tt = ((double)ts.TotalSeconds 
				/ (double)MudTimeConfiguration.Current.RealSecondsPerTick);
			return new MudTimeSpan((long)tt);
		}
		#endregion

		#region String Conversions
		public override string ToString()
		{
			throw new NotImplementedException();
		}
		public static MudTimeSpan Parse(string s)
		{
			throw new NotImplementedException();
		}
		public static explicit operator string(MudTimeSpan ts)
		{
			return ts.ToString();
		}
		public static explicit operator MudTimeSpan(string s)
		{
			return MudTimeSpan.Parse(s);
		}
		#endregion
	}
}
